package com.linewell.license.demo.advice;


import com.alibaba.fastjson.JSON;
import com.linewell.license.demo.annotation.RestWrapper;
import com.linewell.license.platform.common.model.exception.BusinessException;
import com.linewell.license.platform.common.model.exception.NotFoundException;
import com.linewell.license.platform.common.model.exception.RooBaseException;
import com.linewell.license.platform.common.model.result.ResultBaseModel;
import com.linewell.license.platform.common.model.result.ResultCodeBaseConstants;
import com.linewell.license.platform.common.model.result.ResultListModel;
import com.linewell.license.platform.common.model.result.ResultObjectModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * @author
 * Company:
 * Createtime : 2018年8月31日 上午9:32:03
 * Description : springboot全局错误日志捕获
 * 1、全局异常处理（业务异常、容器级异常）
 * 2、全局返回值后处理
 */
@RestControllerAdvice
public class GlobalControllerAdvice implements ResponseBodyAdvice<Object> {


    private Logger logger = LoggerFactory.getLogger(GlobalControllerAdvice.class);

    /**
     * 需要忽略的地址
     */
    private static String[] ignores = new String[]{"/swagger-resources", "/v2/api-docs"};

    /**
     * 是否支持，添加了RestWrapper注解的方法不支持
     */
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return !(returnType.getMethod().isAnnotationPresent(RestWrapper.class));
    }

    /**
     * 默认异常处理，返回500错误
     *
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultObjectModel<String> defaultExceptionHandler(HttpServletRequest req, Exception e) {
        return getExpResponse(req, e);
    }

    /**
     * 无法找到映射handler的异常处理，返回404错误
     *
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResultObjectModel<String> noHandlerFoundExceptionHandler(HttpServletRequest req, Exception e) {
        return getExpResponse(req, e);
    }

    /**
     * 无法找到实体，返回204
     *
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(NotFoundException.class)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public ResultObjectModel<String> notFoundExceptionHandler(HttpServletRequest req, Exception e) {
        return getExpResponse(req, e);
    }

    /**
     * 业务层异常
     *
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public ResultObjectModel<Object> businessExceptionHandler(HttpServletRequest req, Exception e) {
        BusinessException ex = (BusinessException) e;
        // 记录错误信息
        logger.error("request url : " + req.getRequestURL().toString() + " response data : " + ex.getMessage());
        return new ResultObjectModel(ResultCodeBaseConstants.SERVER_MESSAGE, ex.getMessage(), ex.getData());
    }

    /**
     * 异常信息返回
     *
     * @param req
     * @param e
     * @return
     */
    private ResultObjectModel<String> getExpResponse(HttpServletRequest req, Exception e) {
        logger.error(req.getRequestURL().toString() + " " + e.getMessage(), e);
        return new ResultObjectModel(ResultCodeBaseConstants.FAIL, e.getMessage(), null);
    }

    /**
     * 返回正常的信息
     *
     * @param req
     * @param data
     * @return
     */
    private ResultObjectModel<Object> getWrapperResponse(ServerHttpRequest req, Object data) {
        return new ResultObjectModel<>(ResultCodeBaseConstants.SUCCESS, data);
    }

    @ResponseBody
    @ExceptionHandler(RooBaseException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public ResultBaseModel<Object> handlerRooBaseException(RooBaseException e,
                                                           HttpServletRequest request) {
        logger.error("RooBaseException异常!requestUri={}| requestBody={}| errMsg={}", request.getRequestURI(), getRequestBody(), e);
        return new ResultBaseModel<>(e.getErrCode(), e.getErrMsg(), e.getErrMsg());
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {

        if (this.ignoring(request.getURI().toString())) {
            return body;
        }
        if (body instanceof ResultObjectModel || body instanceof ResultListModel || body instanceof ResultBaseModel) {
            return body;
        }
        if (body instanceof Byte) {
            return body;
        }
        if (body instanceof String) {
            return body;
        }
        return getWrapperResponse(request, body);
    }

    private boolean ignoring(String uri) {
        for (String string : ignores) {
            if (uri.contains(string)) {
                return true;
            }
        }
        return false;
    }

    private ThreadLocal<Object> modelHolder = new ThreadLocal<>();

    private String getRequestBody() {
        return JSON.toJSONString(modelHolder.get());
    }

    @InitBinder
    public void initBinder(WebDataBinder webDataBinder) {
        // ModelHolder 初始化0
        modelHolder.set(webDataBinder.getTarget());
    }
}
